package com.example.amusic.service.v3

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
import androidx.media3.common.util.UnstableApi
import androidx.media3.session.CommandButton
import androidx.media3.session.MediaNotification
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaStyleNotificationHelper
import com.example.amusic.R
import com.example.amusic.utils.asArtworkBitmap
import com.google.common.collect.ImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

private const val MusicNotificationId = 1001
private const val MusicNotificationChannelId = "MusicNotificationChannel"

@UnstableApi
class MusicNotificationProvider(
    private val context: Context
) : MediaNotification.Provider {

    private val notificationManager = checkNotNull(context.getSystemService<NotificationManager>())
    private val coroutineScope by lazy {
        CoroutineScope(Dispatchers.Main + SupervisorJob())
    }
    private val ioDispatcher by lazy {
        Dispatchers.IO
    }


    override fun createNotification(
        mediaSession: MediaSession,
        customLayout: ImmutableList<CommandButton>,
        actionFactory: MediaNotification.ActionFactory,
        onNotificationChangedCallback: MediaNotification.Provider.Callback
    ): MediaNotification {
        ensureNotificationChannel()
        val player = mediaSession.player
        val metaData = player.mediaMetadata
        val builder = NotificationCompat.Builder(context, MusicNotificationChannelId)
            .setContentTitle(metaData.title)
            .setContentText(metaData.artist)
            .setSmallIcon(R.mipmap.icon_main)
            .setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession))
            .setContentIntent(mediaSession.sessionActivity)

        getNotificationActions(
            mediaSession,
            customLayout,
            actionFactory,
            playWhenReady = player.playWhenReady
        ).forEach(builder::addAction)

        setupArtwork(
            uri = metaData.artworkUri,
            setLargeIcon = builder::setLargeIcon,
            updateNotification = {
                val notification = MediaNotification(MusicNotificationId, builder.build())
                onNotificationChangedCallback.onNotificationChanged(notification)
            }
        )

        return MediaNotification(MusicNotificationId, builder.build())
    }

    override fun handleCustomCommand(
        session: MediaSession,
        action: String,
        extras: Bundle
    ): Boolean = false

    private fun ensureNotificationChannel() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || notificationManager.getNotificationChannel(
                MusicNotificationChannelId
            ) != null
        ) {
            return
        }
        val notificationChannel = NotificationChannel(
            MusicNotificationChannelId,
            "Now playing",
            NotificationManager.IMPORTANCE_LOW
        )
        notificationManager.createNotificationChannel(notificationChannel)
    }


    private fun getNotificationActions(
        mediaSession: MediaSession,
        customLayout: ImmutableList<CommandButton>,
        actionFactory: MediaNotification.ActionFactory,
        playWhenReady: Boolean
    ) = listOf(
        MusicActions.getFavoriteAction(mediaSession, customLayout, actionFactory),
        MusicActions.getSkipPreviousAction(context, mediaSession, actionFactory),
        MusicActions.getPlayPauseAction(context, mediaSession, actionFactory, playWhenReady),
        MusicActions.getSkipNextAction(context, mediaSession, actionFactory),
        MusicActions.getRepeatShuffleAction(mediaSession, customLayout, actionFactory),
    )

    private fun setupArtwork(
        uri: Uri?,
        setLargeIcon: (Bitmap?) -> Unit,
        updateNotification: () -> Unit
    ) = coroutineScope.launch {
        val bitmap = loadArtworkBitmap(uri)
        setLargeIcon(bitmap)
        updateNotification()
    }

    private suspend fun loadArtworkBitmap(uri: Uri?) = withContext(ioDispatcher) {
        uri?.asArtworkBitmap(context)
    }

    fun cancelCoroutineScope() {
        coroutineScope.cancel()
        ioDispatcher.cancel()
    }
}